import { ref } from 'vue';

import axios from 'axios';
import { ElNotification } from 'element-plus';
import Queue from 'promise-queue-plus';

import { fileUploaderApi } from '#/api';

import md5 from '../lib/md5';

// 上传
// 文件上传分块任务的队列（用于移除文件时，停止该文件的上传队列） key：fileUid value： queue object
const fileUploadChunkQueue = ref({}).value;
/**
 * 获取一个上传任务，没有则初始化一个 状态码200000代表请求成功
 */
const getTaskInfo = async (file) => {
  let task;
  const identifier = await md5(file);
  task = (await fileUploaderApi.taskInfo(identifier)) as any;

  if (!task) {
    const initTaskData = {
      identifier,
      fileName: file.name,
      totalSize: file.size,
      chunkSize: 5 * 1024 * 1024,
    };
    task = (await fileUploaderApi.initTask(initTaskData)) as any;
  }
  return task;
};

/**
 * 上传逻辑处理，如果文件已经上传完成（完成分块合并操作），则不会进入到此方法中
 */

const handleUpload = (file, taskRecord, options) => {
  if (
    file.type !== 'application/zip' &&
    file.name.toLowerCase().endsWith('.shp')
  ) {
    ElNotification.error({
      title: '文件上传错误，请重试',
      message: '文件上传错误，请重试',
    });
    return;
  }

  let uploadedSize = 0; // 已上传的大小
  const totalSize = file.size || 0; // 文件总大小
  const { exitPartList, chunkSize, chunkNum, fileIdentifier } = taskRecord;

  const uploadNext = async (partNumber) => {
    const start = Number(chunkSize) * (partNumber - 1);

    const end = start + Number(chunkSize);
    const blob = file.slice(start, end);

    const data = (await fileUploaderApi.preSignUrl({
      identifier: fileIdentifier,
      partNumber,
    })) as any;
    if (data) {
      await axios.request({
        url: data,
        method: 'PUT',
        data: blob,
        headers: { 'Content-Type': 'application/octet-stream' },
      });
      return {
        partNumber,
        uploadedSize: blob.size,
      };
    }
    throw new Error(`分片${partNumber}， 获取上传地址失败`);
  };

  /**
   * 更新上传进度
   * @param increment 为已上传的进度增加的字节量
   */

  const updateProcess = (increment) => {
    increment = Number(increment);
    const { onProgress } = options;
    const factor = 1000; // 每次增加1000 byte
    let from = 0;
    // 通过循环一点一点的增加进度
    while (from <= increment) {
      from += factor;
      uploadedSize += factor;
      const percent = Math.round((uploadedSize / totalSize) * 100).toFixed(2);
      onProgress({ percent });
    }
  };

  return new Promise((resolve) => {
    const failArr = [];
    const queue = Queue(5, {
      retry: 3, // Number of retries
      retryIsJump: false, // retry now?

      workReject(reason) {
        failArr.push(reason);
      },

      queueEnd() {
        resolve(failArr);
      },
    });

    fileUploadChunkQueue[file.uid] = queue;
    for (let partNumber = 1; partNumber <= chunkNum; partNumber++) {
      const exitPart = (exitPartList || []).find(
        (exitPart: any) => exitPart.partNumber === partNumber,
      );
      if (exitPart) {
        // 分片已上传完成，累计到上传完成的总额中,同时记录一下上次断点上传的大小，用于计算上传速度
        updateProcess(exitPart.size);
      } else {
        queue.push(() =>
          uploadNext(partNumber).then((res) => {
            // 单片文件上传完成再更新上传进度
            updateProcess(res.uploadedSize);
          }),
        );
      }
    }
    if (queue.getLength() === 0) {
      // 所有分片都上传完，但未合并，直接return出去，进行合并操作

      resolve(failArr);
      return;
    }
    queue.start();
  });
};

/**
 * 移除文件列表中的文件
 * 如果文件存在上传队列任务对象，则停止该队列的任务
 */

const handleRemoveFile = (uploadFile) => {
  const queueObject = fileUploadChunkQueue[uploadFile.uid];
  if (queueObject) {
    queueObject.stop();

    fileUploadChunkQueue[uploadFile.uid] = undefined;
  }
};
export default {
  getTaskInfo,
  handleUpload,
  handleRemoveFile,
};
